package com.google.code.ptrends.processors.implementations;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import com.google.code.ptrends.catparsers.ETLItemFactory;
import com.google.code.ptrends.catparsers.utils.IntPair;
import com.google.code.ptrends.common.entities.Category;
import com.google.code.ptrends.common.entities.EventType;
import com.google.code.ptrends.common.entities.Session;
import com.google.code.ptrends.common.utils.FileHelper;
import com.google.code.ptrends.entities.ETLItem;
import com.google.code.ptrends.excel.ExcelReader;
import com.google.code.ptrends.excel.entities.Cell;
import com.google.code.ptrends.excel.entities.CellStyle;
import com.google.code.ptrends.excel.entities.Worksheet;
import com.google.code.ptrends.processors.interfaces.ETLProcessor;

public class RVExcelProcessor implements ETLProcessor {
    private final ExcelReader excelReader;
    private final ETLItemFactory parser;
    private final Session session;

    public RVExcelProcessor(final ExcelReader excelReader, final ETLItemFactory parser,
	    final Session session) {
	this.excelReader = excelReader;
	this.parser = parser;
	this.session = session;

	session.addEvent("RET Processor has been created", EventType.INFO);
    }

    /*
     * Set "Category" cell style
     */
    public CellStyle getCategoryStyle() {
	final CellStyle categoryCellStyle = new CellStyle();

	categoryCellStyle.setFontSize(8);
	categoryCellStyle.setBold(true);
	categoryCellStyle.setItalic(false);
	categoryCellStyle.setUnderlined(false);
	categoryCellStyle.setBackground("FFFFFF");
	categoryCellStyle.setForeground("000000");

	return categoryCellStyle;
    }

    public Worksheet getItemsWorksheet() {
	// Change "Price" to "Прайс-лист" for ret.xls
	return new Worksheet("Прайс-лист");
    }

    /*
     * Locate "Category": find "Category" name and its the first and the last
     * "Category" cell position
     */
    public Map<Category, IntPair> getCategoriesPositions() {
	final List<Integer> index = new ArrayList<Integer>();
	final Map<Category, IntPair> positionMap = new EnumMap<Category, IntPair>(Category.class);
	final CellStyle categoryStyle = getCategoryStyle();
	final Map<Category, Integer> categories = new HashMap<Category, Integer>();
	final Map<Integer, Integer> pairs = new HashMap<Integer, Integer>();
	final Worksheet sheet = getItemsWorksheet();
	final int firstPosition = excelReader.getFirstRowIndex(sheet);
	final int lastPosition = excelReader.getLastRowIndex(sheet);

	/*
	 * Define "Category" cell name and its the first position
	 */
	for (int i = firstPosition; i <= lastPosition; i++) {
	    if (excelReader.getRow(sheet, i) != null) {
		for (Cell cells : excelReader.getRow(sheet, i).getCells()) {
		    /*
		     * If "Category" cell is found, save its value and set row
		     * index as the first "Category" cell position
		     */
		    if (cells.getStyle().equals(categoryStyle)) {
			final String cellValue = cells.getValue();
			if (cellValue != null) {
			    //System.out.println(cellValue);
			    final Category category = CategoryHelper.fromString(cellValue);
			    if (category != Category.UNKNOWN) {
				//System.out.println(cellValue);
				categories.put(category, i + 1);
				index.add(i + 1);
			    }
			}
		    }
		}
	    }
	}

	/*
	 * Define "Category" the last position
	 */
	for (int k = index.get(index.size() - 1); k <= lastPosition; k++) {
	    if (excelReader.getRow(sheet, k) != null) {
		for (Cell cells : excelReader.getRow(sheet, k).getCells()) {
		    if (cells.getStyle().equals(categoryStyle)) {
			index.add(k + 1);
			break;
		    } else if (k == lastPosition) {
			index.add(k + 2);
			break;
		    }
		}
	    }
	}

	/*
	 * Create IntPair map with parameters:
	 * 
	 * key (Integer) - the first "Category" cell position value (Integer) -
	 * the last "Category" cell position
	 */
	for (int k = 0; k < index.size() - 1; k++) {
	    pairs.put(index.get(k), index.get(k + 1) - 2);
	}

	/*
	 * Create map as EnumMap with parameters:
	 * 
	 * key (Category) - "Category" cell name value (IntPair) - the first and
	 * the last "Category" cell position
	 */
	for (Entry<Category, Integer> categoriesEntry : categories.entrySet()) {
	    for (Entry<Integer, Integer> pairsEntry : pairs.entrySet()) {
		if (pairsEntry.getKey().equals(categoriesEntry.getValue())) {
		    positionMap.put(categoriesEntry.getKey(), new IntPair(pairsEntry.getKey(),
			    pairsEntry.getValue()));
		}
	    }
	}

	session.addEvent("Finished building category positions", EventType.INFO);

	return positionMap;
    }

    /*
     * Create aggregated ETLItem collection of all categories
     * 
     * For each "Category" invoke proper parser to create <Category>ETLItem
     * collection and then aggregate all <Category>ETLItem collections
     * 
     * (non-Javadoc)
     * 
     * @see com.google.code.ptrends.processors.ETLProcessor#importData()
     */
    @Override
    public Collection<ETLItem> importData(final int supplierID) {
	final Map<Category, IntPair> map = getCategoriesPositions();
	final List<ETLItem> result = new ArrayList<ETLItem>();
	ArrayList<ETLItem> items = null;

	for (Entry<Category, IntPair> entry : map.entrySet()) {
	    Category category = entry.getKey();
	    IntPair itemsPositions = entry.getValue();
	    int totalItemsCount = Math.abs(itemsPositions.getB() - itemsPositions.getA());
	    session.addEvent(String.format("Started processing category [%s]. Total items: %s",
		    category, totalItemsCount), EventType.INFO);

	    items = new ArrayList<ETLItem>(Arrays.asList(parser.parse(category, supplierID,
		    itemsPositions)));

	    float parseRatio = (float) items.size() / (float) totalItemsCount * 100;
	    if (parseRatio > 100)
		parseRatio = 100;

	    session.addEvent(String.format(
		    "Category [%s] has been processed. Parsed items: %s. Parse ratio is: %s",
		    category, items.size(), parseRatio), EventType.INFO);
	    session.increaseItemsCount(items.size());

	    result.addAll(items);
	}

	return result;
    }

    private static class CategoryHelper {

	private static final String MAP_FILE_NAME = "src/main/resources/com/google/code/ptrends/ret_price_category.string";

	static {
	    init();
	}

	private CategoryHelper() {
	}

	public static Category fromString(final String categoryStr) {
	    final Category category = categoriesMap.get(categoryStr);
	    return category == null ? Category.UNKNOWN : category;
	}

	private static void init() {
	    categoriesMap = new HashMap<String, Category>();

	    String[] lines;
	    try {
		lines = FileHelper.readAllLines(MAP_FILE_NAME);
		categoriesMap.put(lines[0], Category.CPU);
		categoriesMap.put(lines[1], Category.MOTHERBOARD);
		categoriesMap.put(lines[2], Category.VIDEOCARD);
		categoriesMap.put(lines[3], Category.HDD);
		categoriesMap.put(lines[4], Category.SOUNDCARD);
		categoriesMap.put(lines[5], Category.RAM);
		categoriesMap.put(lines[6], Category.CONTROLLER);
		categoriesMap.put(lines[7], Category.MONITOR);
		categoriesMap.put(lines[8], Category.CASE);
		categoriesMap.put(lines[9], Category.DRIVE);
	    } catch (IOException e) {
		LOG.error("Error while loading category names", e);
	    }
	}

	private static Map<String, Category> categoriesMap;
	private static final Logger LOG = Logger.getLogger(CategoryHelper.class);
    }
}